﻿package no.doomsday.aronning.engines.verlets.geometry
{
	import flash.display.Graphics;
	import flash.display.Shape;
	import flash.geom.Point;
	import no.doomsday.aronning.engines.verlets.IRenderable;
	import no.doomsday.aronning.engines.verlets.Verlet;
	
	/**
	 * ...
	 * @author Andreas Rønning
	 */
	public class Hull implements IRenderable
	{
		private var _pointSet:Vector.<Verlet>;
		private var p:Verlet;
		private var prevY:Number;
		private var prevX:Number;
		private var prevHullCalculated:Vector.<Verlet>;
		private var centerOfMass:Point;
		private static var HULLS:Vector.<Hull> = new Vector.<Hull>();
		public static function cleanUp():void {
			for (var i:int = 0; i < HULLS.length; i++) 
			{
				HULLS[i].cleanUp();
			}
		}
		public function Hull(pointSet:Vector.<Verlet>) 
		{
			HULLS.push(this);
			this.pointSet = pointSet;
		}
		public function getHull():Vector.<Verlet> {
			prevHullCalculated = buildHull(pointSet);
			return prevHullCalculated;
		}
		private function crossproduct(p1:Verlet,p2:Verlet,p3:Verlet):Number
		{
			var cp:Number = (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y);
			return cp;
		}
		public function cleanUp():void {
			for (var i:int = pointSet.length; i--; ) {
				if (pointSet[i].dead) {
					pointSet.splice(i, 1);
				}
			}
		}
		private function buildHull(points:Vector.<Verlet>):Vector.<Verlet>
		{
			//Find pivot P, bottommost on Y 
			var pArray:Vector.<Verlet> = points.concat();
			pArray.sort(ascendingY);
			p = pArray.shift();
			
			//Sort Points by angle (with points with equal angle further sorted by distance from P);
			pArray.sort(sortFunc);
			
			var stack:Vector.<Verlet> = new Vector.<Verlet>();
			stack.push(p);
			stack.push(pArray[0]);
			for (var i:int = 0; i < pArray.length; i++) {
				while (stack.length >= 2 && crossproduct(stack[stack.length-2], stack[stack.length-1], pArray[i]) >= 0) {
					stack.pop();
				}
				stack.push(pArray[i]);
			}
			return stack;
		}
		
		private function ascendingY(a:Verlet,b:Verlet):int
		{
			if(a.y!=b.y){
				if (a.y > b.y) return -1;
				if (a.y < b.y) return 1;
			}else {
				if (a.x > b.x) return 1;
				if (a.x < b.x) return -1;
			}
			return 0;
		}
		private function ascendingX(a:Verlet,b:Verlet):int
		{
			if (a.x > b.x) return -1;
			if (a.x < b.x) return 1;
			return 0;
		}
		
		private function sortFunc(a:Verlet, b:Verlet):int
		{
			var diffX1:Number = p.x - a.x;
			var diffY1:Number = p.y - a.y;
			var ang1:Number = Math.atan2(diffY1, diffX1);
			
			var diffX2:Number = p.x - b.x;
			var diffY2:Number = p.y - b.y;
			var ang2:Number = Math.atan2(diffY2, diffX2);
			
			if (ang1 < ang2) {
				return 1;
			}else if (ang2 < ang1) {
				return -1;
			}
			
			var dist1:Number = diffX1 * diffX1 + diffY1 * diffY1;
			var dist2:Number = diffX2 * diffX2 + diffY2 * diffY2;
			if (dist2 < dist1) {
				return 1;
			}
			return -1;
		}
		
		public function get pointSet():Vector.<Verlet> { return _pointSet; }
		
		public function set pointSet(value:Vector.<Verlet>):void 
		{
			_pointSet = value;
		}
		public function scale(scaleX:Number = 1, scaleY:Number = 1):void {
			//TODO scale all points by scaleX and scaleY across the center of mass
		}
		public function translate(x:Number, y:Number):void {
			//TODO move all points by x and y
			for (var i:int = pointSet.length; i--; ) 
			{
				pointSet[i].x += x;
				pointSet[i].y += y;
			}
		}
		public function getCenterOfMass(recalculate:Boolean = false):Point {
			if (recalculate) {
				getHull();
				//TODO calculate a point in the center of the hull
				var tempRender:Shape = new Shape;
				render(tempRender.graphics);
				centerOfMass.x = tempRender.width / 2;
				centerOfMass.y = tempRender.height / 2;
			}
			return centerOfMass;
		}
		public function render(g:Graphics):void
		{
			g.lineStyle(0, 0x0000FF);
			g.beginFill(0x000000, 0.3);
			var polygonPoints:Vector.<Verlet> = getHull();
			for (var i:int = 0; i < polygonPoints.length; i++) 
			{
				if (i == 0) {
					g.moveTo(polygonPoints[i].x, polygonPoints[i].y);
				}else {
					g.lineTo(polygonPoints[i].x, polygonPoints[i].y);
				}
			}
		}
		public function destroy():void {
			pointSet = new Vector.<Verlet>;
			p = new Verlet();
			Hull.cleanUp();
		}
		
	}
	
}